import { assetManager, AssetManager } from "cc";
import { GameCore } from "../../../Base/Class";
import { setClassName } from "../../../Base/Global";
import { Singleton } from "../../../Base/Singleton";
import { IGameSystem } from "../../../Interfaces/IGameSystem";
import { AssetInfo } from "../base/AssetInfo";
import { ResourceLoadClassNameConst, ResourceLoadSpace } from "../_h/Index";


interface IBunleEntity 
{
    assetBundleName : string;
    isLoadDone : boolean;
    tryLoadCount : number;
    errorMsg : string;
    bundle : AssetManager.Bundle;
    refCount : number;
    callBackList : Array<Function>;
}

export class ResourceLoadManager extends GameCore.ClassBase implements ResourceLoadSpace.IResourceLoadManager, IGameSystem.IAwakeSystem, IGameSystem.IUpdateSystem
{
    private static readonly MaxTryLoadBundleCount = 3;
    public static cdnUrl : string = null;
    public baseUrl: string;
    private bundleCacheMap = new Map<string, IBunleEntity>();
    private waitUploadBundleNameList = new Array<string>();
    private waitLoadDoneBundleList = new Array<string>();

    Awake(): void {
       // this.bundleCacheMap.set(AssetInfo.resourceBundle, {resources});
    }

    Update(): void {
        this.ProcessBundleLoadDoneList();
        this.ProcessUploadBundleList();
    }

    private ProcessBundleLoadDoneList()
    {
        var index = 0;
        var count = this.waitLoadDoneBundleList.length;
        while(index < count)
        {
            ++index;
            var assetBundleName = this.waitLoadDoneBundleList.shift();
            var bundleEntity = this.GetBundleEntity(assetBundleName);
            if(bundleEntity != null)
            {
                if(bundleEntity.isLoadDone)
                {
                    var callBackList = bundleEntity.callBackList;
                    var bundle = bundleEntity.bundle;
                    for(var i = 0; i < callBackList.length; i++){
                        var callBack = callBackList[i];
                        callBack(bundle);
                        ++bundleEntity.refCount;
                    }
                    bundleEntity.callBackList.slice();
                }
                else
                {
                    this.waitLoadDoneBundleList.push(assetBundleName)
                }
            }
        }
    }

    private ProcessUploadBundleList()
    {
        var index = 0
        var count = this.waitUploadBundleNameList.length;
        while(index < count)
        {
            ++index;
            var assetBundleName = this.waitUploadBundleNameList.shift();
            var bundleEntity = this.GetBundleEntity(assetBundleName);
            if(!bundleEntity.isLoadDone)
            {
                this.waitUploadBundleNameList.push(assetBundleName);
            }
            else if(bundleEntity.refCount == 0)
            {
                // 卸载
                console.log("upload bundle! " + bundleEntity.assetBundleName);
            }
        }
    }

    public LoadAsset(asset : AssetInfo, callBack : (bundle : AssetManager.Bundle)=>void):void
    {
        var assetInfo  = asset as AssetInfo;
        if(assetInfo.assetBundleName == null)
        {
            var errorMsg = "assetbundleName canot be null!  " + assetInfo.assetName;
            console.error(errorMsg);
            callBack(null);
            return;
        }

        var assetBundleName = assetInfo.assetBundleName;
        var index = this.waitLoadDoneBundleList.indexOf(assetBundleName);
        if(index < 0)
        {
            this.waitLoadDoneBundleList.push(assetBundleName);
        }

        var assetBundleName = assetInfo.assetBundleName;
        var bunleEntity : IBunleEntity = null;

        if(this.bundleCacheMap.has(assetBundleName))
        {
            var oInfo = this.bundleCacheMap.get(assetBundleName);
            bunleEntity = oInfo;
            oInfo.callBackList.push(callBack);
        }
        else
        {
            var info : IBunleEntity = {
                assetBundleName : assetBundleName,
                isLoadDone : false,
                tryLoadCount : 0,
                errorMsg : null,
                bundle : null,
                refCount : 0,
                callBackList : new Array<Function>(),
            };
            
            this.bundleCacheMap.set(assetBundleName, info)
            bunleEntity = info;
            this.LoadBundle(assetBundleName);
        }
        bunleEntity.callBackList.push(callBack);
    }

    private GetBundleEntity(assetBundleName : string):IBunleEntity
    {
        if(!this.bundleCacheMap.has(assetBundleName)){
            return null;
        }
        return this.bundleCacheMap.get(assetBundleName);
    }

    private LoadBundle(assetBundleName : string)
    {
        if(!this.bundleCacheMap.has(assetBundleName)){
            return;
        }

        var bunleEntity = this.bundleCacheMap.get(assetBundleName);
        if(bunleEntity.tryLoadCount >= ResourceLoadManager.MaxTryLoadBundleCount)
        {
            console.log("can not load bundle by assetBundleName : "+ assetBundleName);
            bunleEntity.isLoadDone = true;
            return;
        }
        
        // 下载
        if(assetBundleName == AssetInfo.resourceBundle)
        {
            if(bunleEntity.bundle == null)
            {
                bunleEntity.bundle = AssetManager.instance.resources;
            }
            bunleEntity.isLoadDone = true;
        }
        else
        {
            var url = ResourceLoadManager.cdnUrl == null ? assetBundleName : (ResourceLoadManager.cdnUrl + "/" + assetBundleName);
            assetManager.loadBundle(url, (err:Error, bundle : AssetManager.Bundle)=>{
                if (err != null)
                {
                    var errorMsg = err + " from url:" + url;
                    var bundleEntity = Singleton.Instance(ResourceLoadManager).GetBundleEntity(assetBundleName);
                    if(bundleEntity != null)
                    {
                        bundleEntity.errorMsg = errorMsg;
                        console.error(errorMsg);
                        Singleton.Instance(ResourceLoadManager).LoadBundle(assetBundleName);
                    }
                    // else
                    // {
                    //     bundle.release(url);
                    // }
                }
                else
                {
                    Singleton.Instance(ResourceLoadManager).LoadBundleCallBack(assetBundleName, bundle);
                }
            });
        }
    }

    private LoadBundleCallBack(assetBundleName: string, bundle: AssetManager.Bundle) {
        if(this.bundleCacheMap.has(assetBundleName))
        {
            var oCache = this.bundleCacheMap.get(assetBundleName);
            oCache.isLoadDone = true;
            oCache.bundle = bundle;
        }
    }

    public UnloadAsset(bundle: AssetManager.Bundle)
    {
        if(bundle == null)
        {
            return;
        }
        
        var assetBunelName : string = null;
        this.bundleCacheMap.forEach((bundleEntity, _assetBunelName, map)=>{
            if(bundleEntity.isLoadDone && bundleEntity.errorMsg == null && bundle == bundleEntity.bundle)
            {
                assetBunelName = _assetBunelName;
            }
        });

        if(assetBunelName == null)
        {
            console.error("some errer is happend! can not found bundle in bundleCacheMap");
        }

        if(assetBunelName == AssetInfo.resourceBundle)
        {
            return;
        }

        var bundleEntity = this.bundleCacheMap.get(assetBunelName)
        --bundleEntity.refCount;
        if(bundleEntity.refCount < 0)
        {
            console.error("some errer is happend! can not found bundle in bundleCacheMap" + bundleEntity.refCount);
        }

        if(bundleEntity.refCount <= 0)
        {
            var index = this.waitUploadBundleNameList.indexOf(assetBunelName);
            if(index < 0)
            {
                this.waitUploadBundleNameList.push(assetBunelName);
            }
        }
    }
}
setClassName(ResourceLoadManager, ResourceLoadClassNameConst.ResourceLoadManagerClassName)